package com.veng.springbootdemo;

import com.veng.springbootdemo.po.SysPermission;
import com.veng.springbootdemo.po.SysRole;
import com.veng.springbootdemo.po.UserInfo;
import com.veng.springbootdemo.service.UserInfoService;
import com.veng.springbootdemo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 配置 realm securityManager 和 shiroFilterFactoryBean
 * realm 是用来 身份认证和权限认证得
 * SecurityManger:SecurityManager是Shiro核心，主要协调Shiro内部的各种安全组件
 * shiroFilterFactoryBean 是用来配置 访问接口权限以及登录地址错误地址.
 */
@Configuration
public class ShiroConfig {
    @Bean
    public MyRealm myRealm(){
        MyRealm myRealm = new MyRealm();
        return  myRealm;
    }
    @Bean
    public SecurityManager securityManager(MyRealm myRealm){
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(myRealm);
        return defaultSecurityManager;
    }
    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，以为在
     * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
     *
     * Filter Chain定义说明 1、一个URL可以配置多个Filter，使用逗号分隔 2、当设置多个过滤器时，全部验证通过，才视为通过
     * 3、部分过滤器可指定参数，如perms，roles
     *
     */

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

//        拦截器
        Map<String, String> filterChainDefinitionMap  = new LinkedHashMap<>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/ajaxLogin", "anon");
//        filterChainDefinitionMap.put("/upload", "anon");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
//        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 开启Shiro注解(如@RequiresRoles,@RequiresPermissions),
     * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    class MyRealm extends AuthorizingRealm{
        @Autowired
        private UserInfoService userInfoService;

        /**
         * 权限认证
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//            UserInfo userInfo = (UserInfo)principalCollection.getPrimaryPrincipal();
            String username = (String)SecurityUtils.getSubject().getPrincipal();
            UserInfo userInfo = userInfoService.findUserInfoByUsername(username);
            List<SysRole> roleList = userInfo.getRoleList();
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            for (SysRole sysRole : roleList) {
                List<SysPermission> permissions = sysRole.getPermissions();
                for (SysPermission permission : permissions) {
                    simpleAuthorizationInfo.addStringPermission(permission.getPermission());
                }
            }
            return simpleAuthorizationInfo;
        }

        /**
         * 身份认证
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("doGetAuthenticationInfo");
            String username = (String) authenticationToken.getPrincipal();
//            authenticationToken.getPrincipal()
//            UsernamePasswordToken userInfoToken = (UsernamePasswordToken) authenticationToken;
//            String username = userInfoToken.getUsername();
            String password = new String((char[]) authenticationToken.getCredentials());
//            char[] password = new String(userInfoToken.getPassword())
            UserInfo userInfoByUsername = userInfoService.findUserInfoByUsername(username);

            if (userInfoByUsername == null) {
                throw new UnknownAccountException("用户名不正确");
            }else{
                String password1 = userInfoByUsername.getPassword();
                if (!password.equals(password1)){
                    throw new IncorrectCredentialsException("密码不正确");
                }
            }
            return new SimpleAuthenticationInfo(username,password,getName());
        }
    }
}
